home *** CD-ROM | disk | FTP | other *** search
- /* Upper half of IP, consisting of send/receive primitives, including
- * fragment reassembly, for higher level protocols.
- * Not needed when running as a standalone gateway.
- * Copyright 1991 Phil Karn, KA9Q
- */
- #include "global.h"
- #include "mbuf.h"
- #include "timer.h"
- #include "internet.h"
- #include "netuser.h"
- #include "iface.h"
- #include "pktdrvr.h"
- #include "icmp.h"
- #include "commands.h"
-
- #if !defined(_lint)
- static char rcsid[] OPTIONAL = "$Id: ip.c,v 1.18 1997/09/07 21:18:28 root Exp root $";
- #endif
-
- static struct mbuf *fraghandle (struct ip * ip, struct mbuf * bp);
- static void ip_timeout (void *arg);
- static void free_reasm (struct reasm * rp);
- static void freefrag (struct frag * fp);
- static struct reasm *lookup_reasm (struct ip * ip);
- static struct reasm *creat_reasm (struct ip * ip);
- static struct frag *newfrag (int16 offset, int16 last, struct mbuf * bp);
-
- struct mib_entry Ip_mib[20] =
- {
- { "", {0} },
- { "ipForwarding", {1} },
- { "ipDefaultTTL", {MAXTTL} },
- { "ipInReceives", {0} },
- { "ipInHdrErrors", {0} },
- { "ipInAddrErrors", {0} },
- { "ipForwDatagrams", {0} },
- { "ipInUnknownProtos", {0} },
- { "ipInDiscards", {0} },
- { "ipInDelivers", {0} },
- { "ipOutRequests", {0} },
- { "ipOutDiscards", {0} },
- { "ipOutNoRoutes", {0} },
- { "ipReasmTimeout", {TLB} },
- { "ipReasmReqds", {0} },
- { "ipReasmOKs", {0} },
- { "ipReasmFails", {0} },
- { "ipFragOKs", {0} },
- { "ipFragFails", {0} },
- { "ipFragCreates", {0} },
- };
-
-
- struct reasm *Reasmq;
- static struct raw_ip *Raw_ip = NULLRIP;
- static int raw_ip_inuse = TNOS_MUTEX_UNLOCKED;
-
- #define INSERT 0
- #define APPEND 1
- #define PREPEND 2
-
-
-
- /* Send an IP datagram. Modeled after the example interface on p 32 of
- * RFC 791
- */
- int
- ip_send (source, dest, protocol, tos, ttl, bp, length, id, df)
- uint32 source; /* source address */
- uint32 dest; /* Destination address */
- char protocol; /* Protocol */
- char tos; /* Type of service */
- char ttl; /* Time-to-live */
- struct mbuf *bp; /* Data portion of datagram */
- int16 length; /* Optional length of data portion */
- int16 id; /* Optional identification */
- char df; /* Don't-fragment flag */
- {
- struct mbuf *tbp;
- struct ip ip; /* IP header */
- static int16 id_cntr = 0; /* Datagram serial number */
- struct phdr phdr;
-
- ipOutRequests++;
-
- if (source == INADDR_ANY)
- source = locaddr (dest);
- if (length == 0 && bp != NULLBUF)
- length = len_p (bp);
- if (id == 0)
- id = id_cntr++;
- if (ttl == 0)
- ttl = (char) ipDefaultTTL;
-
- /* Fill in IP header */
- ip.version = IPVERSION;
- ip.tos = tos;
- ip.length = IPLEN + length;
- ip.id = id;
- ip.offset = 0;
- ip.flags.mf = 0;
- ip.flags.df = df;
- ip.flags.congest = 0;
- ip.ttl = ttl;
- ip.protocol = protocol;
- ip.source = source;
- ip.dest = dest;
- ip.optlen = 0;
- if ((tbp = htonip (&ip, bp, IP_CS_NEW)) == NULLBUF) {
- free_p (bp);
- return -1;
- }
- if ((bp = pushdown (tbp, sizeof (phdr))) == NULLBUF) {
- free_p (tbp);
- return -1;
- }
- if (ismyaddr (ip.dest)) {
- /* Pretend it has been sent by the loopback interface before
- * it appears in the receive queue
- */
- phdr.iface = &Loopback;
- Loopback.ipsndcnt++;
- Loopback.rawsndcnt++;
- Loopback.lastsent = secclock ();
- } else
- phdr.iface = NULLIF;
- phdr.type = CL_NONE;
- memcpy (&bp->data[0], (char *) &phdr, sizeof (phdr));
- enqueue (&Hopper, bp);
- return 0;
- }
-
-
-
- /* Reassemble incoming IP fragments and dispatch completed datagrams
- * to the proper transport module
- */
- void
- ip_recv (iface, ip, bp, rxbroadcast)
- struct iface *iface; /* Incoming interface */
- struct ip *ip; /* Extracted IP header */
- struct mbuf *bp; /* Data portion */
- int rxbroadcast; /* True if received on subnet broadcast address */
- {
- /* Function to call with completed datagram */
- register struct raw_ip *rp;
- struct mbuf *bp1, *tbp;
- int rxcnt = 0;
- register struct iplink *ipp;
-
- /* If we have a complete packet, call the next layer
- * to handle the result. Note that fraghandle passes back
- * a length field that does NOT include the IP header
- */
- if ((bp = fraghandle (ip, bp)) == NULLBUF)
- return; /* Not done yet */
-
- ipInDelivers++;
-
- kmutex_lock (&raw_ip_inuse);
-
- for (rp = Raw_ip; rp != NULLRIP; rp = rp->next) {
- if (rp->protocol != ip->protocol)
- continue;
- rxcnt++;
- /* Duplicate the data portion, and put the header back on */
- (void) dup_p (&bp1, bp, 0, len_p (bp));
- if (bp1 != NULLBUF && (tbp = htonip (ip, bp1, IP_CS_OLD)) != NULLBUF) {
- enqueue (&rp->rcvq, tbp);
- if (rp->r_upcall != NULLVFP ((struct raw_ip *)))
- (*rp->r_upcall) (rp);
- } else {
- free_p (bp1);
- }
- }
- kmutex_unlock (&raw_ip_inuse);
-
- /* Look it up in the transport protocol table */
- for (ipp = Iplink; ipp->funct != NULL; ipp++) {
- if (ipp->proto == ip->protocol)
- break;
- }
- if (ipp->funct != NULL) {
- /* Found, call transport protocol */
- (*ipp->funct) (iface, ip, bp, rxbroadcast);
- } else {
- /* Not found */
- if (rxcnt == 0) {
- /* Send an ICMP Protocol Unknown response... */
- ipInUnknownProtos++;
- /* ...unless it's a broadcast */
- if (!rxbroadcast)
- (void) icmp_output (ip, bp, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, NULLICMP);
- }
- free_p (bp);
- }
- }
-
-
-
- #ifdef ENCAP
- /* Handle IP packets encapsulated inside IP */
- void
- ipip_recv (iface, ip, bp, rxbroadcast)
- struct iface *iface OPTIONAL; /* Incoming interface */
- struct ip *ip OPTIONAL; /* Extracted IP header */
- struct mbuf *bp; /* Data portion */
- int rxbroadcast OPTIONAL; /* True if received on subnet broadcast address */
- {
- struct phdr phdr;
- struct mbuf *tbp;
-
- if ((tbp = pushdown (bp, sizeof (phdr))) == NULLBUF) {
- free_p (bp);
- return;
- }
- bp = tbp;
- phdr.iface = &Encap;
- phdr.type = CL_NONE;
- memcpy (&bp->data[0], (char *) &phdr, sizeof (phdr));
- enqueue (&Hopper, bp);
- }
- #endif
-
-
-
- /* Process IP datagram fragments
- * If datagram is complete, return it with ip->length containing the data
- * length (MINUS header); otherwise return NULLBUF
- */
- static
- struct mbuf *
- fraghandle (ip, bp)
- struct ip *ip; /* IP header, host byte order */
- struct mbuf *bp; /* The fragment itself */
- {
- register struct reasm *rp; /* Pointer to reassembly descriptor */
- struct frag *lastfrag, *nextfrag, *tfp;
- struct mbuf *tbp;
- int16 i;
- int16 last; /* Index of first byte beyond fragment */
-
- last = (int16) (ip->offset + ip->length - (IPLEN + ip->optlen));
-
- rp = lookup_reasm (ip);
- if (ip->offset == 0 && !ip->flags.mf) {
- /* Complete datagram received. Discard any earlier fragments */
- if (rp != NULLREASM) {
- free_reasm (rp);
- ipReasmOKs++;
- }
- return bp;
- }
- ipReasmReqds++;
- if (rp == NULLREASM) {
- /* First fragment; create new reassembly descriptor */
- if ((rp = creat_reasm (ip)) == NULLREASM) {
- /* No space for descriptor, drop fragment */
- ipReasmFails++;
- free_p (bp);
- return NULLBUF;
- }
- }
- /* Keep restarting timer as long as we keep getting fragments */
- start_detached_timer (&rp->timer);
-
- /* If this is the last fragment, we now know how long the
- * entire datagram is; record it
- */
- if (!ip->flags.mf)
- rp->length = last;
-
- /* Set nextfrag to the first fragment which begins after us,
- * and lastfrag to the last fragment which begins before us
- */
- lastfrag = NULLFRAG;
- for (nextfrag = rp->fraglist; nextfrag != NULLFRAG; nextfrag = nextfrag->next) {
- if (nextfrag->offset > ip->offset)
- break;
- lastfrag = nextfrag;
- }
- /* Check for overlap with preceeding fragment */
- if (lastfrag != NULLFRAG && ip->offset < lastfrag->last) {
- /* Strip overlap from new fragment */
- i = lastfrag->last - ip->offset;
- (void) pullup (&bp, (unsigned char *) 0, i);
- if (bp == NULLBUF)
- return NULLBUF; /* Nothing left */
- ip->offset += i;
- }
- /* Look for overlap with succeeding segments */
- for (; nextfrag != NULLFRAG; nextfrag = tfp) {
- tfp = nextfrag->next; /* save in case we delete fp */
-
- if (nextfrag->offset >= last)
- break; /* Past our end */
- /* Trim the front of this entry; if nothing is
- * left, remove it.
- */
- i = last - nextfrag->offset;
- (void) pullup (&nextfrag->buf, (unsigned char *) 0, i);
- if (nextfrag->buf == NULLBUF) {
- /* superseded; delete from list */
- if (nextfrag->prev != NULLFRAG)
- nextfrag->prev->next = nextfrag->next;
- else
- rp->fraglist = nextfrag->next;
- if (tfp->next != NULLFRAG)
- nextfrag->next->prev = nextfrag->prev;
- freefrag (nextfrag);
- } else
- nextfrag->offset = last;
- }
- /* Lastfrag now points, as before, to the fragment before us;
- * nextfrag points at the next fragment. Check to see if we can
- * join to either or both fragments.
- */
- i = INSERT;
- if (lastfrag != NULLFRAG && lastfrag->last == ip->offset)
- i |= APPEND;
- if (nextfrag != NULLFRAG && nextfrag->offset == last)
- i |= PREPEND;
- /*lint -save -e613 */
- switch (i) {
- case INSERT: /* Insert new desc between lastfrag and nextfrag */
- tfp = newfrag (ip->offset, last, bp);
- tfp->prev = lastfrag;
- tfp->next = nextfrag;
- if (lastfrag != NULLFRAG)
- lastfrag->next = tfp; /* Middle of list */
- else
- rp->fraglist = tfp; /* First on list */
- if (nextfrag != NULLFRAG)
- nextfrag->prev = tfp;
- break;
- case APPEND: /* Append to lastfrag */
- append (&lastfrag->buf, bp);
- lastfrag->last = last; /* Extend forward */
- break;
- case PREPEND: /* Prepend to nextfrag */
- tbp = nextfrag->buf;
- nextfrag->buf = bp;
- append (&nextfrag->buf, tbp);
- nextfrag->offset = ip->offset; /* Extend backward */
- break;
- case (APPEND | PREPEND):
- /* Consolidate by appending this fragment and nextfrag
- * to lastfrag and removing the nextfrag descriptor
- */
- append (&lastfrag->buf, bp);
- append (&lastfrag->buf, nextfrag->buf);
- nextfrag->buf = NULLBUF;
- lastfrag->last = nextfrag->last;
-
- /* Finally unlink and delete the now unneeded nextfrag */
- lastfrag->next = nextfrag->next;
- if (nextfrag->next != NULLFRAG)
- nextfrag->next->prev = lastfrag;
- freefrag (nextfrag);
- break;
- default:
- break;
- }
- /*lint -restore */
- if (rp->fraglist->offset == 0 && rp->fraglist->next == NULLFRAG && rp->length != 0) {
- /* We've gotten a complete datagram, so extract it from the
- * reassembly buffer and pass it on.
- */
- bp = rp->fraglist->buf;
- rp->fraglist->buf = NULLBUF;
- /* Tell IP the entire length */
- ip->length = (int16) (rp->length + (IPLEN + ip->optlen));
- free_reasm (rp);
- ipReasmOKs++;
- return bp;
- } else
- return NULLBUF;
- }
-
-
-
- /* Arrange for receipt of raw IP datagrams */
- struct raw_ip *
- raw_ip (protocol, r_upcall)
- int protocol;
- void (*r_upcall) (struct raw_ip *);
- {
- register struct raw_ip *rp;
-
- rp = (struct raw_ip *) callocw (1, sizeof (struct raw_ip));
-
- kmutex_lock (&raw_ip_inuse);
-
- rp->protocol = protocol;
- rp->r_upcall = r_upcall;
-
- rp->next = Raw_ip;
- Raw_ip = rp;
- kmutex_unlock (&raw_ip_inuse);
- return rp;
- }
-
-
-
- /* Free a raw IP descriptor */
- void
- del_ip (struct raw_ip *rpp)
- {
- struct raw_ip *rplast = NULLRIP;
- register struct raw_ip *rp;
-
- /* Do sanity check on arg */
- for (rp = Raw_ip; rp != NULLRIP; rplast = rp, rp = rp->next)
- if (rp == rpp)
- break;
- if (rp == NULLRIP)
- return; /* Doesn't exist */
-
- kmutex_lock (&raw_ip_inuse);
-
- /* Unlink */
- if (rplast != NULLRIP)
- rplast->next = rp->next;
- else
- Raw_ip = rp->next;
- kmutex_unlock (&raw_ip_inuse);
-
- /* Free resources */
- free_q (&rp->rcvq);
- free ((char *) rp);
- }
-
-
-
- static struct reasm *
- lookup_reasm (struct ip *ip)
- {
- register struct reasm *rp;
- struct reasm *rplast = NULLREASM;
-
- for (rp = Reasmq; rp != NULLREASM; rplast = rp, rp = rp->next) {
- if (ip->id == rp->id && ip->source == rp->source && ip->dest == rp->dest && ip->protocol == rp->protocol) {
- if (rplast != NULLREASM) {
- /* Move to top of list for speed */
- rplast->next = rp->next;
- rp->next = Reasmq;
- Reasmq = rp;
- }
- return rp;
- }
- }
- return NULLREASM;
- }
-
-
-
- /* Create a reassembly descriptor,
- * put at head of reassembly list
- */
- static struct reasm *
- creat_reasm (register struct ip *ip)
- {
- register struct reasm *rp;
-
- if ((rp = (struct reasm *) callocw (1, sizeof (struct reasm))) == NULLREASM)
- return rp;/* No space for descriptor */
-
- rp->source = ip->source;
- rp->dest = ip->dest;
- rp->id = ip->id;
- rp->protocol = ip->protocol;
- set_timer (&rp->timer, (long) (ipReasmTimeout * 1000L));
- rp->timer.func = ip_timeout;
- rp->timer.arg = rp;
-
- rp->next = Reasmq;
- Reasmq = rp;
- return rp;
- }
-
-
-
- /* Free all resources associated with a reassembly descriptor */
- static void
- free_reasm (struct reasm *r)
- {
- register struct reasm *rp;
- struct reasm *rplast = NULLREASM;
- register struct frag *fp;
-
- for (rp = Reasmq; rp != NULLREASM; rplast = rp, rp = rp->next)
- if (r == rp)
- break;
- if (rp == NULLREASM)
- return; /* Not on list */
-
- stop_timer (&rp->timer);
- /* Remove from list of reassembly descriptors */
- if (rplast != NULLREASM)
- rplast->next = rp->next;
- else
- Reasmq = rp->next;
-
- /* Free any fragments on list, starting at beginning */
- while ((fp = rp->fraglist) != NULLFRAG) {
- rp->fraglist = fp->next;
- free_p (fp->buf);
- free ((char *) fp);
- }
- free ((char *) rp);
- }
-
-
-
- /* Handle reassembly timeouts by deleting all reassembly resources */
- static void
- ip_timeout (void *arg)
- {
- register struct reasm *rp;
-
- rp = (struct reasm *) arg;
- free_reasm (rp);
- ipReasmFails++;
- }
-
-
-
- /* Create a fragment */
- static struct frag *
- newfrag (int16 offset, int16 last, struct mbuf *bp)
- {
- struct frag *fp;
-
- if ((fp = (struct frag *) callocw (1, sizeof (struct frag))) == NULLFRAG) {
- /* Drop fragment */
- free_p (bp);
- return NULLFRAG;
- }
- fp->buf = bp;
- fp->offset = offset;
- fp->last = last;
- return fp;
- }
-
-
-
- /* Delete a fragment, return next one on queue */
- static void
- freefrag (struct frag *fp)
- {
- free_p (fp->buf);
- free ((char *) fp);
- }
-
-
-
- #ifdef MSDOS
- /* In red alert mode, blow away the whole reassembly queue. Otherwise crunch
- * each fragment on each reassembly descriptor
- */
- void
- ip_garbage (int red)
- {
- struct reasm *rp, *rp1;
- struct frag *fp;
- struct raw_ip *rwp;
-
- /* Run through the reassembly queue */
- for (rp = Reasmq; rp != NULLREASM; rp = rp1) {
- rp1 = rp->next;
- if (red)
- free_reasm (rp);
- else {
- for (fp = rp->fraglist; fp != NULLFRAG; fp = fp->next)
- mbuf_crunch (&fp->buf);
- }
- }
- /* Run through the raw IP queue */
- for (rwp = Raw_ip; rwp != NULLRIP; rwp = rwp->next)
- mbuf_crunch (&rwp->rcvq);
- }
- #endif
-